<template>
	<div class="max-w-4xl mx-auto p-6">
		<h1 class="text-2xl font-bold mb-4">视频倒放（浏览器端）</h1>

		<div class="bg-white shadow rounded p-4 mb-6">
			<p class="text-sm text-gray-600 mb-4">将视频倒放并导出新的文件。注意：浏览器端仅支持倒放视频，音频将被去除。</p>

			<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
				<div>
					<label class="block text-sm font-medium text-gray-700 mb-2">选择视频文件</label>
					<input type="file" accept="video/*" @change="onFileChange" class="mb-3" />

					<div v-if="originalUrl">
						<h4 class="text-sm font-medium text-gray-700 mb-2">原始预览</h4>
						<video ref="originalVideo" :src="originalUrl" controls class="w-full bg-black rounded"></video>
						<p class="text-xs text-gray-500 mt-2">时长：{{formatTime(duration)}} · 大小：{{ formatBytes(originalSize) }}</p>
					</div>
					<div v-else class="w-full h-48 bg-gray-50 rounded flex items-center justify-center text-gray-400">还没有选择视频</div>
				</div>

				<div>
					<h4 class="text-sm font-medium text-gray-700 mb-2">倒放设置</h4>
					<div class="mb-3 text-sm text-gray-600">音频将被去除，仅支持倒放视频。</div>

					<div class="flex space-x-3 mt-4">
						<button @click="startReverse" :disabled="!readyToStart || isProcessing" class="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 disabled:opacity-50">开始倒放导出</button>
						<button @click="stopReverse" :disabled="!isProcessing" class="px-4 py-2 bg-red-600 text-white rounded hover:bg-red-700 disabled:opacity-50">停止</button>
						<button @click="downloadResult" :disabled="!resultBlob" class="px-4 py-2 bg-gray-700 text-white rounded hover:bg-gray-800 disabled:opacity-50">下载</button>
					</div>

					<div v-if="isProcessing" class="mt-4">
						<p class="text-sm text-gray-600">进度：{{ Math.floor(progress * 100) }}%</p>
						<div class="w-full bg-gray-200 rounded h-2 mt-2 overflow-hidden"><div :style="{width: (progress*100)+'%'}" class="h-2 bg-blue-600"></div></div>
					</div>

					<div v-if="resultBlob" class="mt-4">
						<p class="text-sm text-gray-600">完成：大小 {{ formatBytes(resultSize) }}</p>
						<video :src="resultUrl" controls class="w-full bg-black rounded mt-2"></video>
					</div>
				</div>
			</div>
		</div>
	</div>
</template>

<script setup>
import { ref, onUnmounted, nextTick, computed } from 'vue'

const originalFile = ref(null)
const originalUrl = ref(null)
const originalVideo = ref(null)
const duration = ref(0)
const originalSize = ref(0)

const isProcessing = ref(false)
const progress = ref(0)

let canvas = null
let canvasCtx = null
let drawRaf = null
let captureStream = null
let mediaRecorder = null
let recordedChunks = []

const resultBlob = ref(null)
const resultUrl = ref(null)
const resultSize = ref(0)

function formatBytes(bytes) {
	if (!bytes) return '0 B'
	const units = ['B','KB','MB','GB']
	let i = 0
	let v = bytes
	while (v >= 1024 && i < units.length-1) { v/=1024; i++ }
	return v.toFixed(2) + ' ' + units[i]
}

function formatTime(s) {
	if (!s) return '0:00'
	const m = Math.floor(s/60)
	const sec = Math.floor(s%60).toString().padStart(2,'0')
	return `${m}:${sec}`
}

function onFileChange(e) {
	stopReverse()
	const file = e.target.files && e.target.files[0]
	if (!file) return
	originalFile.value = file
	originalSize.value = file.size
	if (originalUrl.value) URL.revokeObjectURL(originalUrl.value)
	originalUrl.value = URL.createObjectURL(file)
	resultBlob.value = null
	resultUrl.value = null

	nextTick(() => {
		const vid = originalVideo.value
		if (!vid) return
		vid.onloadedmetadata = () => { duration.value = vid.duration || 0 }
		vid.load()
	})
}

const readyToStart = computed(() => !!originalFile.value && !!originalUrl.value && !isProcessing.value)

let reverseStopped = false
async function startReverse() {
	if (!originalFile.value) { alert('请先选择视频文件'); return }
	if (!('MediaRecorder' in window) || !HTMLCanvasElement.prototype.captureStream) { alert('当前浏览器不支持必要的 API（MediaRecorder 或 canvas.captureStream）'); return }

	const vid = originalVideo.value
	if (!vid) return
	if (!vid.duration || !vid.videoWidth) await new Promise(res => { vid.onloadedmetadata = res; vid.load() })

	const w = vid.videoWidth || 1280
	const h = vid.videoHeight || Math.round(w * 9/16)

	canvas = document.createElement('canvas')
	canvas.width = w
	canvas.height = h
	canvasCtx = canvas.getContext('2d')

	const fps = 25
	captureStream = canvas.captureStream(fps)

	let mimeType = ''
	if (MediaRecorder.isTypeSupported('video/webm;codecs=vp9')) mimeType = 'video/webm;codecs=vp9'
	else if (MediaRecorder.isTypeSupported('video/webm;codecs=vp8')) mimeType = 'video/webm;codecs=vp8'
	else mimeType = 'video/webm'

	try {
		recordedChunks = []
		mediaRecorder = new MediaRecorder(captureStream, { mimeType })
	} catch (err) { console.error('无法创建 MediaRecorder：', err); alert('无法创建 MediaRecorder（可能是不支持的编码或参数）'); return }

	mediaRecorder.ondataavailable = e => { if (e.data && e.data.size > 0) recordedChunks.push(e.data) }
	mediaRecorder.onstop = onRecorderStop

	// 采集所有帧并实时显示进度，允许中断
	let frames = []
	reverseStopped = false
	isProcessing.value = true
	progress.value = 0

	stopReverse._handler = () => { reverseStopped = true }

	vid.currentTime = 0
	vid.muted = true

	await new Promise(resolve => {
		vid.onseeked = collectFrame
		function collectFrame() {
			if (reverseStopped) {
				vid.onseeked = null
				resolve()
				return
			}
			if (vid.currentTime >= vid.duration) {
				vid.onseeked = null
				resolve()
				return
			}
			canvasCtx.drawImage(vid, 0, 0, w, h)
			frames.push(canvas.toDataURL('image/webp'))
			progress.value = Math.min(0.5, vid.currentTime/vid.duration * 0.5)
			vid.currentTime = Math.min(vid.currentTime + 1/fps, vid.duration)
		}
		collectFrame()
	})

	// 倒序渲染帧并录制
	let idx = frames.length - 1
	if (frames.length === 0) {
		isProcessing.value = false
		progress.value = 0
		return
	}
	mediaRecorder.start(250)
	async function renderReverseFrames() {
		for (; idx >= 0; idx--) {
			if (reverseStopped) break
			await new Promise(r => setTimeout(r, 1000/fps))
			let img = new window.Image()
			img.src = frames[idx]
			await new Promise(res => { img.onload = res })
			canvasCtx.clearRect(0, 0, w, h)
			canvasCtx.drawImage(img, 0, 0, w, h)
			progress.value = 0.5 + 0.5 * (1 - idx/frames.length)
		}
		if (mediaRecorder && mediaRecorder.state !== 'inactive') mediaRecorder.stop()
	}
	renderReverseFrames()
}

function onRecorderStop() {
	const blob = new Blob(recordedChunks, { type: 'video/webm' })
	resultBlob.value = blob
	resultSize.value = blob.size
	if (resultUrl.value) URL.revokeObjectURL(resultUrl.value)
	resultUrl.value = URL.createObjectURL(blob)
	cleanup()
	isProcessing.value = false
	progress.value = 1
}

function stopReverse() {
	if (stopReverse._handler) stopReverse._handler();
	// 只停止 mediaRecorder，由 onRecorderStop 负责 cleanup 和状态
	setTimeout(() => {
		try { if (mediaRecorder && mediaRecorder.state !== 'inactive') mediaRecorder.stop() } catch(e) {}
	}, 0)
	if (drawRaf) { cancelAnimationFrame(drawRaf); drawRaf = null }
	// 不立即 cleanup，等待 onRecorderStop 处理
	if (originalVideo.value) { try { originalVideo.value.pause(); originalVideo.value.currentTime = 0; originalVideo.value.playbackRate = 1 } catch(e){} }
	// isProcessing/progress 由 onRecorderStop 处理
}

function cleanup() {
	if (captureStream) { captureStream.getTracks().forEach(t => { try{ t.stop() }catch(e){} }); captureStream = null }
	if (canvas) { try { canvas.width = 0; canvas.height = 0 } catch(e){} canvas = null; canvasCtx = null }
	mediaRecorder = null
}

function downloadResult() {
	if (!resultBlob.value) return
	const a = document.createElement('a')
	a.href = resultUrl.value
	a.download = `reverse_${new Date().toISOString().replace(/[:.]/g,'-')}.webm`
	document.body.appendChild(a)
	a.click()
	a.remove()
}

onUnmounted(() => { try { if (originalUrl.value) URL.revokeObjectURL(originalUrl.value) } catch(e){} try { if (resultUrl.value) URL.revokeObjectURL(resultUrl.value) } catch(e){} stopReverse() })
</script>

<style scoped>
/* 保持风格 */
</style>